/* Copyright 2009 Requirements Management System 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.mags.remas.view.widgets;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.jface.viewers.ViewerRow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.PlatformUI;
import org.mags.remas.model.entity.Entity;
import org.mags.remas.model.entity.EntityRelationship;
import org.mags.remas.model.entity.ViewType;

/**
 * 
 * @author Mario Gaitan
 *
 */
public class RelationshipTreeViewer extends TreeViewer {
	private IElementComparer comparer;
	private List<EntityRelationship> elementsDroped;
	private EntityRelationship targetToDrop;
	private EntityRelationship root;
	private RelationshipTreeViewerListener treeViewerListener;
	private TreeColumn column;
	private boolean intercalateColor = false;
	private boolean showCursor = false;
	private boolean allowShowColumn = false;
	private boolean loadColumnListener = false;
	private boolean showColumn = false;
	final private ViewType viewType;
	
	
	public void findAndSelectRelationship(EntityRelationship fER) {
		String hierarchicalPath = fER.getHierarchicalPath();
		if(!hierarchicalPath.equals("/")) {
			String[] items = hierarchicalPath.split("/");
			int i = 0;
			for(String item: items) {
				if(i>0) {
					EntityRelationship entityRelationship=new EntityRelationship();
					entityRelationship.setEntity(new Entity());
					entityRelationship.setEntityParent(new Entity());
					entityRelationship.getEntity().setEntityId(Long.valueOf(item));
					if(i==1) 
						entityRelationship.getEntityParent().setEntityId(getRoot().getEntity().getEntityId());
					else {
						String parentItem = items[i-1];
						entityRelationship.getEntityParent().setEntityId(Long.valueOf(parentItem));
					}
					expandToLevel(entityRelationship, 1);
					selectItem(entityRelationship);
				}
				i++;
			}
			
		}
	}
	public ViewType getViewType() {
		return viewType;
	}
	private void loadColumnListener() {
		if(!loadColumnListener) {
			SelectionListener selectionListener = new SelectionListener() {
				@Override
				public void widgetDefaultSelected(SelectionEvent e) {
				}
				@Override
				public void widgetSelected(SelectionEvent e) {
					if(allowShowColumn) {
						if(showColumn&&column==e.widget)
							showColumn = false;
						else
							showColumn = true;
						column = (TreeColumn)e.widget;
						getTree().redraw();
					}
				}
			};
			for(TreeColumn column: getTree().getColumns()) 
				column.addSelectionListener(selectionListener);
			loadColumnListener = true;
		}
	}
	@Override
	public void setCellEditors(CellEditor[] editors) {
		loadColumnListener();
		super.setCellEditors(editors);
	}
	public boolean isAllowShowColumn() {
		return allowShowColumn;
	}
	public void setAllowShowColumn(boolean allowShowColumn) {
		this.allowShowColumn = allowShowColumn;
	}
	public boolean isIntercalateColor() {
		return intercalateColor;
	}
	public void setIntercalateColor(boolean intercalateColor) {
		this.intercalateColor = intercalateColor;
	}
	public boolean isShowCursor() {
		return showCursor;
	}
	public void setShowCursor(boolean showCursor) {
		this.showCursor = showCursor;
	}
	public Widget doFindItem(EntityRelationship data) {
		return super.doFindItem(data);
	}	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void selectItem(EntityRelationship item) {
		Widget widget = super.doFindItem(item);
		if(widget!=null) {
			List items = new ArrayList();
			items.add(widget);
			this.setSelection(items);
		}
	}
	public void refreshParent(EntityRelationship element) {
		EntityRelationship parent = doFindParentData(element);
		if(parent!=null) 
			refresh(parent);
	}	
	public ViewerRow getViewerRowFromItem(TreeItem item) {
		return super.getViewerRowFromItem(item);
	}
	public boolean existsData(EntityRelationship entityRelationship) {
		Widget widget = super.doFindItem(entityRelationship);
		return (widget!=null);
	}
	public List<EntityRelationship> doFindChildrenData(EntityRelationship entityRelationship) {
		Widget widget = super.doFindItem(entityRelationship);
		if(widget==null)
			return null;
		else {
			List<EntityRelationship> childrenList = new ArrayList<EntityRelationship>();
			for(TreeItem i:((TreeItem)widget).getItems()) {
				EntityRelationship er = (EntityRelationship)i.getData();
				er.setParent(entityRelationship);
				childrenList.add(er);
			}
			return childrenList;
		}
	}
	public EntityRelationship doFindParentData(EntityRelationship entityRelationship) {
		Widget widget = super.doFindItem(entityRelationship);
		if(widget==null)
			return null;
		else {
			EntityRelationship temp = (EntityRelationship)widget.getData();
			if(temp.getEntityParent().getEntityId()==root.getEntity().getEntityId())
				return root;
			if(((TreeItem)widget).getParentItem()==null)
				return null;
			else
				return  (EntityRelationship)((TreeItem)widget).getParentItem().getData();
		}
	}
	public EntityRelationship doFindData(EntityRelationship entityRelationship) {
		if(entityRelationship.getEntityRelationshipId()==root.getEntityRelationshipId())
			return root;
		Widget widget = super.doFindItem(entityRelationship);
		if(widget==null)
			return null;
		else {
			EntityRelationship er = (EntityRelationship)widget.getData();
			EntityRelationship parent = doFindParentData(er);
			er.setParent(parent);
			return er;
		}
	}
	public EntityRelationship doFindData(long entityRelationshipId) {
		EntityRelationship entityRelationship = new EntityRelationship();
		entityRelationship.setEntityRelationshipId(entityRelationshipId);
		return this.doFindData(entityRelationship);
	}
	public void updateData(EntityRelationship entityRelationship) {
		Widget widget = super.doFindItem(entityRelationship);
		if(widget!=null) {
			widget.setData(entityRelationship);
			super.doUpdateItem((TreeItem)widget, entityRelationship, true);
		} else if(root!=null&&entityRelationship.getEntityRelationshipId()==root.getEntityRelationshipId())
			this.root = entityRelationship;
	}
	public void addRelationshipTreeViewerListener(RelationshipTreeViewerListener treeViewerListener) {
		this.treeViewerListener = treeViewerListener;
	}
	public EntityRelationship getRoot() {
		return root;
	}
	public void setRoot(EntityRelationship root) {
		this.root = root;
		super.setInput(root);
	}
	public TreeItem getSelectedRow() {
		TreeItem[] selection = getTree().getSelection();
		if(selection!=null&&selection.length>0)
			return selection[0];
		return null;
	}
	public void setSelectedColumn(TreeColumn column) {
		this.column = column;
		getTree().redraw();
	}
	public void setSelectedColumn(int column) {
		this.column = getTree().getColumns()[column];
		getTree().redraw();
	}
	public TreeColumn getSelectedColumn() {
		return column;
	}
	public RelationshipTreeViewer(Composite parent, int style, ViewType viewType) {
		this(parent, style, viewType, true, true);
	}
	public RelationshipTreeViewer(Composite parent, int style, ViewType viewType, boolean dragSupport, boolean dropSupport) {
		super(parent, style | (SWT.MULTI|SWT.V_SCROLL|SWT.H_SCROLL));
		this.viewType = viewType;
		if(dragSupport)
			super.addDragSupport(DND.DROP_COPY|DND.DROP_MOVE, new Transfer[]{PlainObjectTransfer.getInstance()},
				new DragSourceAdapter() {
					private List<EntityRelationship> selectedElements;
					public void dragFinished(DragSourceEvent event) {
						if (!event.doit)
							return;
					}
					public void dragSetData(DragSourceEvent event) {
						if (PlainObjectTransfer.getInstance().isSupportedType(event.dataType)) 
							event.data = selectedElements;
					}
					public void dragStart(DragSourceEvent event) {
						selectedElements = getSelectedElements();
						event.doit = (selectedElements.size()>0);
						targetToDrop = null;
						elementsDroped = null;
					}			
				}
			);
		if(dropSupport)
			super.addDropSupport(DND.DROP_COPY|DND.DROP_MOVE, new Transfer[]{PlainObjectTransfer.getInstance()},
					new ViewerDropAdapter(this) {
						@SuppressWarnings("unchecked")
						public boolean performDrop(Object data) {
							targetToDrop = (EntityRelationship)getCurrentTarget();
							if (targetToDrop == null) 
								targetToDrop = root;
							else {
								EntityRelationship parent = doFindParentData(targetToDrop);
								targetToDrop.setParent(parent);
							}
							elementsDroped = (List<EntityRelationship>)data;
							if(elementsDroped==null||elementsDroped.size()==0) {
								targetToDrop = null;
								elementsDroped = null;
								return false;
							}
							if(treeViewerListener!=null) {
								return treeViewerListener.dragAndDrop(targetToDrop, elementsDroped);
							}
							return false;
						}
						public boolean validateDrop(Object target, int op, TransferData type) {
							return PlainObjectTransfer.getInstance().isSupportedType(type);
						}					
					}
				);
		super.addSelectionChangedListener(
			new ISelectionChangedListener() {
				public void selectionChanged(SelectionChangedEvent event) {
					EntityRelationship first = getSelectedElement();
					if(treeViewerListener!=null&&first!=null)
						treeViewerListener.select(first, getSelectedElements());
				}
			});
		super.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				EntityRelationship first = getSelectedElement();
				if(treeViewerListener!=null&&first!=null) 
					treeViewerListener.doubleClick(first);	
			}
		});
		super.getTree().addKeyListener(new KeyListener() {
			@Override
			public void keyPressed(KeyEvent e) {
				EntityRelationship first = getSelectedElement();
				if(first!=null&&treeViewerListener!=null)
					treeViewerListener.keyPressed(first, e);
				if(showCursor&&getTree().getColumnCount()>0) {
					if(column!=null) {
						if(e.keyCode==SWT.ARROW_LEFT&&getTree().indexOf(column)>0) {
							column = getTree().getColumn(getTree().indexOf(column)-1);
							e.doit = false;
						}
						else if(e.keyCode==SWT.ARROW_RIGHT&&getTree().indexOf(column)<getTree().getColumnCount()-1) {
							column = getTree().getColumn(getTree().indexOf(column)+1);
							e.doit = false;
						}
					} else {
						if(e.keyCode==SWT.ARROW_LEFT) {
							column = getTree().getColumn(0);
							e.doit = false;
						}
						else if(e.keyCode==SWT.ARROW_RIGHT) {
							column = getTree().getColumn(getTree().getColumnCount()-1);
							e.doit = false;
						}											
					}
					getTree().redraw();
				}
			}
			@Override
			public void keyReleased(KeyEvent e) {
			}
		});
		super.getTree().addMouseListener(new MouseListener() {
			@Override
			public void mouseDoubleClick(MouseEvent e) {
			}
			@Override
			public void mouseDown(MouseEvent e) {
				Point pt = new Point(e.x, e.y);
				TreeItem row = getSelectedRow();				
				if(showCursor&&getTree().getColumnCount()>0) {
					if(row!=null) {
						int lineWidth = getTree().getLinesVisible() ? getTree().getGridLineWidth() : 0;
						for (int i = 0; i < getTree().getColumnCount(); i++) {
							Rectangle rect = row.getBounds(i);
							rect.width += lineWidth;
							rect.height += lineWidth;
							if (rect.contains(pt)) {
								column = getTree().getColumn(i);
								break;
							}
						}
						if (column == null) 
							column = getTree().getColumn(0);
						getTree().redraw();
					}	
				}				
				if(e.button!=1&&getTree().getItem(pt)==null) 
					getTree().setSelection(new TreeItem[0]);
			}
			@Override
			public void mouseUp(MouseEvent e) {
			}
		});
		super.setComparer(this.getComparer());
		final Color evenColor = new Color(PlatformUI.getWorkbench().getDisplay(), 235, 235, 255);
		final Color oddColor = new Color(PlatformUI.getWorkbench().getDisplay(), 255, 255, 255);
		final Color selColor = PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
		Listener paintListener = new Listener() {
			public void handleEvent(Event event) {
				if(showCursor||intercalateColor) {
			        switch (event.type) {
			        case SWT.MeasureItem: {
			        	if(intercalateColor) {
				          if(event.index!=0 && event.index!=2) {
					          TreeItem item = (TreeItem) event.item;
					          String text = item.getText(event.index);
					          Point size = event.gc.textExtent(text);
					          event.width = size.x;
					          event.height = Math.max(event.height, size.y);
				          }
			        	}
			          break;
			        }
			        case SWT.PaintItem: {
		        		TreeItem item = (TreeItem) event.item;
		        		ViewerRow row = getViewerRowFromItem(item);
			        	if(event.index!=0 && event.index!=2) {
							String text = item.getText(event.index);
							Point size = event.gc.textExtent(text);
							int offset2 = event.index == 0 ? Math.max(0, (event.height - size.y) / 2) : 0;
							event.gc.drawText(text, event.x, event.y + offset2, true);
			        	}		        		
			        	if(showColumn&&column!=null&&getTree().indexOf(column)==event.index) {		        		
			        			row.setBackground(event.index,selColor);
			        	} else if(intercalateColor) {				        	
				        	ViewerRow belowRow = row.getNeighbor(ViewerRow.ABOVE, false);
				        	if(belowRow==null)
				        		row.setBackground(event.index, evenColor);
				        	else {
				        		Color color = belowRow.getBackground(event.index);
				        		if(color.getBlue()==evenColor.getBlue()
				        			&& color.getGreen()==evenColor.getGreen()
				        			&& color.getRed()==evenColor.getRed()) {
				        			row.setBackground(event.index, oddColor);		        			
				        		} else {
				        			row.setBackground(event.index, evenColor);		        			
				        		}
				        	}
			        	}
			        	if(showCursor&&column!=null&&getTree().indexOf(column)==event.index) {
			        		TreeItem selItem = getSelectedRow();
		        			if(selItem==item) {
			        			Rectangle rectangle = row.getBounds(event.index);
			        			event.gc.drawRectangle(rectangle.x, rectangle.y, rectangle.width-1, rectangle.height-1);
		        			}	
			        	}
			          break;
			        }
			        case SWT.EraseItem: {
			        	if(intercalateColor) {
				        	if(event.index!=0 && event.index!=2)
				        		event.detail &= ~SWT.FOREGROUND;
			        	}
			          break;
			        }
			      }
		        }
		     }
		};		
		getTree().addListener(SWT.MeasureItem, paintListener);
		getTree().addListener(SWT.PaintItem, paintListener);
		getTree().addListener(SWT.EraseItem, paintListener);
	}
	public EntityRelationship getTargetToDrop() {
		return targetToDrop;
	}
	public List<EntityRelationship> getElementsDroped() {
		return elementsDroped;
	}	
	public IElementComparer getComparer() {
		if(comparer==null)
			comparer = new EntityRelationshipComparer();
		return comparer;
	}
	public void setComparer(IElementComparer comparer) {
		this.comparer = comparer;
	}	
	class EntityRelationshipComparer implements IElementComparer {
		public boolean equals(Object a, Object b) {
			if(a instanceof EntityRelationship && b instanceof EntityRelationship) {
				EntityRelationship _a = (EntityRelationship)a;
				EntityRelationship _b = (EntityRelationship)b;
				if(_a.getEntityRelationshipId()!=0&&_b.getEntityRelationshipId()!=0)
					return _a.getEntityRelationshipId()==_b.getEntityRelationshipId();
				else
					return (_a.getEntity().getEntityId()==_b.getEntity().getEntityId());
			}
			return false;
		}
		public int hashCode(Object element) {
			return 0;
		}
	};	
	@SuppressWarnings("unchecked")
	public List<EntityRelationship> getSelectedElements() {
		ISelection selection = super.getSelection();
		if(selection!=null && selection instanceof IStructuredSelection) {
			Iterator<EntityRelationship> iterator = ((IStructuredSelection)selection).iterator();
			List<EntityRelationship> list=new ArrayList<EntityRelationship>();
			while(iterator.hasNext()) {
				EntityRelationship i = iterator.next();
				EntityRelationship parent = this.doFindParentData(i);
				i.setParent(parent);
				list.add(i);
			}
			return list;
		}
		return null;
	}
	public EntityRelationship getSelectedElement() {
		ISelection selection = super.getSelection();
		if(selection!=null && selection instanceof IStructuredSelection) {
			IStructuredSelection structuredSelection = (IStructuredSelection)selection;
			if(structuredSelection.isEmpty())
				return root;
			else {
				EntityRelationship entityRelationship = (EntityRelationship)structuredSelection.getFirstElement();
				EntityRelationship parent = this.doFindParentData(entityRelationship);
				if(parent!=null)
					entityRelationship.setParent(parent);
				return entityRelationship;
			}
		}
		else
			return root;
	}	
}
